/* Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above notice and this permission notice shall be included in all copies
 * or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
/* File for "Basic Shapes" lesson of the OpenGL tutorial on
 * www.videotutorialsrock.com
 */



#include <iostream>
#include <stdlib.h> //Needed for "exit" function

//Include OpenGL header files, so that we can use OpenGL
#ifdef __APPLE__
#include <OpenGL/OpenGL.h>
#include <GLUT/glut.h>
#else
#include <glut.h>
#endif

#include "imageloader.h"
using namespace std;

float _cameraAngleV = 45.0f;
float _cameraAngleH = 0.0f;
float _zPos = -4.0f;
float _xPos = 0.0f;

void tiltUp(float& value) {
	if (value > 5.0)
	  value -= 1.2f;
	glutPostRedisplay();
}

void tiltDown(float& value) {
	if (value < 70.0)
	  value += 1.2f;
	glutPostRedisplay();
}

void panRight(float& value) {
	value += 1.2f;
	if (value > 360)
		value -= 360;
	glutPostRedisplay();
}

void panLeft(float& value) {
	value -= 1.2f;
	if (value < -360)
		value += 360;
	glutPostRedisplay();
}

void moveForward(float& value) {
	if (value < 4.0)
	  value += 0.2f;
	glutPostRedisplay();
}

void moveLeft(float& value) {
	if (value < 4.0)
	  value += 0.2f;
	glutPostRedisplay();
}

void moveBack(float& value) {
	if (value > -4.0)
	  value -= 0.2f;
	glutPostRedisplay();
}

void moveRight(float& value) {
	if (value > -4.0)
	  value -= 0.2f;
	glutPostRedisplay();
}

void moveToDest(int x, int y) {
	_xPos = x;
	_zPos = y;
	glutPostRedisplay();
}
//Called when a key is pressed
void handleKeypress(unsigned char key, //The key that was pressed
					int x, int y) {    //The current mouse coordinates
	switch (key) {
		case 27: //Escape key
			exit(0); //Exit the program
		case 119:
			moveForward(_zPos);
			break;
		case 97:
			moveLeft(_xPos);
			break;
		case 115:
			moveBack(_zPos);
			break;
		case 100:
			moveRight(_xPos);
			break;
		case 105:
			tiltUp(_cameraAngleV);
			break;
		case 106:
			panLeft(_cameraAngleH);
			break;
		case 107:
			tiltDown(_cameraAngleV);
			break;
		case 108:
			panRight(_cameraAngleH);
			break;
	}
}

GLuint loadTexture(Image* image) {
	GLuint textureId;
	glGenTextures(1, &textureId); //Make room for our texture
	glBindTexture(GL_TEXTURE_2D, textureId); //Tell OpenGL which texture to edit
	//Map the image to the texture
	glTexImage2D(GL_TEXTURE_2D,                //Always GL_TEXTURE_2D
				 0,                            //0 for now
				 GL_RGB,                       //Format OpenGL uses for image
				 image->width, image->height,  //Width and height
				 0,                            //The border of the image
				 GL_RGB, //GL_RGB, because pixels are stored in RGB format
				 GL_UNSIGNED_BYTE, //GL_UNSIGNED_BYTE, because pixels are stored
				                   //as unsigned numbers
				 image->pixels);               //The actual pixel data
	return textureId; //Returns the id of the texture
}

GLuint _textureId;

//Initializes 3D rendering
void initRendering() {
	//Makes 3D drawing work when something is in front of something else
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_COLOR_MATERIAL);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_LIGHT1);
	glEnable(GL_NORMALIZE); //auto normalize normals
	glShadeModel(GL_SMOOTH);

	Image* image = loadBMP("terrain.bmp");
	_textureId = loadTexture(image);
	delete image;

	glClearColor(0.7f, 0.9f, 1.0f, 1.0f);
}

//Called when the window is resized
void handleResize(int w, int h) {
	//Tell OpenGL how to convert from coordinates to pixel values
	glViewport(0, 0, w, h);
	
	glMatrixMode(GL_PROJECTION); //Switch to setting the camera perspective
	
	//Set the camera perspective
	glLoadIdentity(); //Reset the camera
	gluPerspective(45.0,                  //The camera angle
				   (double)w / (double)h, //The width-to-height ratio
				   1.0,                   //The near z clipping coordinate
				   200.0);                //The far z clipping coordinate
}



//Draws the 3D scene
void drawScene() {
	//Clear information from last draw
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
	glMatrixMode(GL_MODELVIEW); //Switch to the drawing perspective
	glLoadIdentity(); //Reset the drawing perspective
	
	glTranslatef(0.0f, 0.0f, -10.0f);

	//Add ambient light
	GLfloat ambientColor[] = {0.2f, 0.2f, 0.2f, 1.0f}; //change color
	glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientColor);

	/*
	//Positioned light
	GLfloat lightColor0[] = {0.5f, 0.5f, 0.5f, 1.0f}; //color
	GLfloat lightPos0[] = {4.0f, 0.0f, 8.0f, 1.0f}; // positioned
	glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0);
	glLightfv(GL_LIGHT0, GL_POSITION, lightPos0);

	//Add directed light
	GLfloat lightColor1[] = {0.5f, 0.2f, 0.2f, 1.0f}; //color
	GLfloat lightPos1[] = {4.0f, 0.0f, 8.0f, 0.0f}; // directed (0.0f)
	glLightfv(GL_LIGHT1, GL_DIFFUSE, lightColor1);
	glLightfv(GL_LIGHT1, GL_POSITION, lightPos1);
	*/
	
	glRotatef(_cameraAngleV, 1.0f, 0.0f, 0.0f);
	//glColor3f(0.1f, 0.0f, 0.1f);
	glRotatef(_cameraAngleH, 0.0f, 1.0f, 0.0f);
	glTranslatef(_xPos, 0.0f, _zPos);

	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, _textureId);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

	glBegin(GL_QUADS); //Begin quadrilateral coordinates
	
	//Frontv
	glNormal3f(0.0f, 1.0f, 0.0f);
	glTexCoord2f(0.0f, 0.0f);
	glVertex3f(-4.0f, 0.0f, 4.0f);
	glTexCoord2f(1.0f, 0.0f);
	glVertex3f(4.0f, 0.0f, 4.0f);
	glTexCoord2f(1.0f, 1.0f);
	glVertex3f(4.0f, 0.0f, -4.0f);
	glTexCoord2f(0.0f, 1.0f);
	glVertex3f(-4.0f, 0.0f, -4.0f);

	glEnd(); 
	
	glutSwapBuffers(); //Send the 3D scene to the screen
}

int main(int argc, char** argv) {
	//Initialize GLUT
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowSize(600, 600); //Set the window size
	
	//Create the window
	glutCreateWindow("My first Terrain");
	initRendering(); //Initialize rendering
	
	//Set handler functions for drawing, keypresses, and window resizes
	glutDisplayFunc(drawScene);
	glutKeyboardFunc(handleKeypress);
	glutReshapeFunc(handleResize);
	
	
	//glutTimerFunc(25, update, 0);
	glutMainLoop(); //Start the main loop.  glutMainLoop doesn't return.
	return 0; //This line is never reached
}









